//////////////////////////////////////////////////////////////////////////

#ifndef _UDPSERVER_H_
#include "udpserver.h"
#endif

//////////////////////////////////////////////////////////////////////////

#ifndef _BOOSTSERVICE_H_
#include "boostservice.h"
#endif

#ifndef _SCOPEGUARD_H_
#include "scopeguard.h"
#endif

#include <iostream>

//////////////////////////////////////////////////////////////////////////


UDPServer::UDPServer()
: m_Socket( BoostService::GetIOService() )
{
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::Start( int _port )
{
    SetupUDPSocket(_port);
    StartReceiveUDP();
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::AddConnection( ServerConnection& _connection )
{
    ScopeGuard guard(m_ConnectionsByIDs);
    m_ConnectionsByIDs.insert( std::make_pair(_connection.GetID(), &_connection) );
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::RemoveConnection( ServerConnection& _connection )
{
    ScopeGuard guard(m_ConnectionsByIDs);
    m_ConnectionsByIDs.erase( _connection.GetID() );
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::SetupUDPSocket( int _port )
{
    m_Socket.open( boost::asio::ip::udp::v4() );
    m_Socket.bind( boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), _port) );
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::StartReceiveUDP()
{
    // Recv data through UDP
    m_Socket.async_receive_from( boost::asio::buffer(m_RecvBufferUDP, MAX_BUFFER_SIZE),
        m_UDPRecvEndpoint,
        boost::bind( &UDPServer::HandleRecvUDP, this,
        boost::asio::placeholders::error,
        boost::asio::placeholders::bytes_transferred ) );
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::HandleRecvUDP( const boost::system::error_code& _error, size_t _bytesTransferred )
{
    // Check for errors
    if (_error)
    {
        // Some strange bugs...
        if (_error.value() == 10022 || _error.value() == 10061)
            return StartReceiveUDP();
        else
            throw "Unknown UDP receive error";
    }

    // First byte is a connection ID
    int clientID = m_RecvBufferUDP[0];

    RedirectUDPToServerConnection(clientID,
                                  m_RecvBufferUDP + 1,
                                  _bytesTransferred - 1);

    // Start receive UDP again
    StartReceiveUDP();
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::RedirectUDPToServerConnection( int _connectionID,
                                               const char* _data,
                                               size_t _dataSize )
{
    ScopeGuard guard(m_ConnectionsByIDs);
    ConnectionsByIDs::iterator it = m_ConnectionsByIDs.find(_connectionID);
    if ( it == m_ConnectionsByIDs.end() )
    {
        std::cout<< "No connection with such ID" <<std::endl;
        return;
    }

    ServerConnection& connection = *(it->second);
    connection.SetUDPEndpoint( m_UDPRecvEndpoint );
    connection.HandleRecvUDP(_data, _dataSize);
}


//////////////////////////////////////////////////////////////////////////


void UDPServer::SendUDP( const NetworkPacket& _packet,
                         const boost::asio::ip::udp::endpoint& _endpoint )
{
    m_Socket.send_to( boost::asio::buffer( _packet.Data(), _packet.Size() ),
                      _endpoint );
}


//////////////////////////////////////////////////////////////////////////
